﻿
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;


namespace MTP.Serialize.Remote
{
    public class ClientDispatchProxy : DispatchProxy
    {


        private readonly MethodInfo methodInfo = typeof(ClientDispatchProxy).GetMethod("ConvertType");

        private Lazy<RemoteOptions> remoteOptions;

        private string ServiceName;

        internal static T Proxy<T>(string serviceName, Func<RemoteOptions> initialization)
        {
            T result = Create<T, ClientDispatchProxy>();
            var self = (result as ClientDispatchProxy);
            self.remoteOptions = new Lazy<RemoteOptions>(initialization);
            self.ServiceName = serviceName;
            return result;
        }

        private Stream Execute(MethodInfo targetMethod, Stream args)
        {
            var options = remoteOptions.Value;
            if (!MTPCoreUtil.TryParseMtpUrl(options.MtpUrl, out int port, out string hostname))
            {
                throw new FormatException("RemoteOptions:MtpUrl");
            }

            var header = MTPCoreUtil.GetHeaderString(new Dictionary<string, string>() { { "service", ServiceName }, { "action", targetMethod.Name } });

            var mtpClient = MtpFactory.GetClient(hostname, port, options.GetCipher());

            return mtpClient.SendAsync(args, header).GetAwaiter().GetResult();
        }

        protected override object Invoke(MethodInfo targetMethod, object[] args)
        {
            if (typeof(Task).IsAssignableFrom(targetMethod.ReturnType))
            {
                return GetTaskResult(targetMethod.ReturnType, () => DeserializeObject(targetMethod, args, targetMethod.ReturnType.GenericTypeArguments.FirstOrDefault()));
            }
            return DeserializeObject(targetMethod, args, targetMethod.ReturnType);
        }

        private object GetTaskResult(Type returnType, Func<object> func)
        {
            if (returnType.GenericTypeArguments.Length == 0)
            {
                return Task.Run(func);
            }
            return methodInfo.MakeGenericMethod(returnType.GenericTypeArguments[0]).Invoke(null, new[] { func });
        }




        public static Task<T> ConvertType<T>(Func<object> func)
        {
            return Task.Run(() => (T)func());
        }



        /// <summary>
        /// 反序列化 扩展以后再说
        /// </summary>
        /// <param name="result"></param>
        /// <param name="returnType"></param>
        /// <returns></returns>
        private object DeserializeObject(MethodInfo targetMethod, object[] args, Type returnType)
        {
            var result = Execute(targetMethod, MTPSerialize.SerializeStreamArray(args));
            if (result == null || returnType == null || returnType.FullName == "System.Void")
            {
                return null;
            }
            var array = MTPSerialize.DeserializeSplit(result);
            if (array[0] == null)
            {
                return null;
            }
            if (array.Count > 1)
            {
                var ps = targetMethod.GetParameters();
                int j = 1;
                for (int i = 0; i < ps.Length; i++)
                {
                    if (ps[i].IsOut || ps[i].ParameterType.IsByRef)
                    {
                        args[i] = MTPSerialize.Deserialize(array[j], ps[i].ParameterType);
                        j++;
                    }
                }
            }
            return MTPSerialize.Deserialize(array[0], returnType);
        }


    }
}
